﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace EpiDashboard.Controls
{
    /// <summary>
    /// Interaction logic for VariablesDisplayControl.xaml
    /// </summary>
    public partial class VariablesDisplayControl : UserControl
    {
        private DataView dv;
        private BackgroundWorker worker;

        public DashboardHelper DashboardHelper { get; set; }

        private delegate void DisplayNumericResultsDelegate(NumericResults results);
        private delegate void DisplayBooleanResultsDelegate(BooleanResults results);
        private delegate void DisplayDateResultsDelegate(DateResults results);

        public DataGridSelectionUnit SelectionUnit
        {
            get
            {
                return dataGridMain.SelectionUnit;
            }
            set
            {
                dataGridMain.SelectionUnit = value;
            }
        }

        public VariablesDisplayControl()
        {
            InitializeComponent();
        }

        public VariablesDisplayControl(DataView dv)
        {
            InitializeComponent();
            this.dv = dv;
        }

        public void SetDataView(DataView dv)
        {
            this.dv = dv;
        }

        public void AttachDashboardHelper(DashboardHelper dashboardHelper)
        {
            this.DashboardHelper = dashboardHelper;
        }

        public void Refresh()
        {
            dataGridMain.DataContext = dv;

            List<DataGridColumn> columnsToRemove = new List<DataGridColumn>();

            foreach (DataGridColumn dgc in dataGridMain.Columns)
            {
                if (dgc.Header.ToString().Equals("SYSTEMDATE"))
                {
                    columnsToRemove.Add(dgc);
                }
            }

            foreach (DataGridColumn dgc in columnsToRemove)
            {
                dataGridMain.Columns.Remove(dgc);
            }
        }

        private void dataGridMain_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
        }

        private void dataGridMain_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
        {
            if (e.Column.Header.ToString().Equals("GlobalRecordId") ||
                e.Column.Header.ToString().Equals("RECSTATUS") ||
                e.Column.Header.ToString().Equals("RecStatus") ||
                e.Column.Header.ToString().Equals("UniqueKey") ||
                e.Column.Header.ToString().Equals("FKEY"))
            {
                e.Cancel = true;
            }
        }

        private void dataGridMain_AutoGeneratedColumns(object sender, EventArgs e)
        {
            List<DataGridColumn> columnsToRemove = new List<DataGridColumn>();

            foreach (DataGridColumn dgc in dataGridMain.Columns)
            {
                if (dgc.Header.ToString().Equals("SYSTEMDATE"))
                {
                    columnsToRemove.Add(dgc);
                }
            }

            foreach (DataGridColumn dgc in columnsToRemove)
            {
                dataGridMain.Columns.Remove(dgc);
            }
        }

        private void dataGridMain_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 0)
            {
                panelVariables.Visibility = System.Windows.Visibility.Collapsed;
                return;
            }
            else
            {
                panelVariables.Visibility = System.Windows.Visibility.Visible;
            }

            lblMin.Content = "Min: ";
            lblMax.Content = "Max: ";
            lblSum.Content = "Sum: ";
            lblMean.Content = "Mean: ";

            lblTrue.Content = "True: ";
            lblFalse.Content = "False: ";

            lblMinDate.Content = "Min: ";
            lblMaxDate.Content = "Max: ";

            foreach(var item in e.AddedItems) 
            {
                if(item is DataRowView)
                {
                    DataRow row = (item as DataRowView).Row;

                    string dataType = row["datatype"].ToString();
                    string variableName = row["columnname"].ToString();
                    tblockContent.Text = variableName + " (" + dataType + ")";

                    switch (dataType.Replace("System.", String.Empty))
                    {
                        case "String":
                            panelString.Visibility = System.Windows.Visibility.Visible;
                            panelNumber.Visibility = System.Windows.Visibility.Collapsed;
                            panelBoolean.Visibility = System.Windows.Visibility.Collapsed;
                            panelDate.Visibility = System.Windows.Visibility.Collapsed;
                            break;
                        case "Byte":
                        case "Int16":
                        case "Int32":
                        case "Int64":
                        case "Single":
                        case "Double":
                        case "Decimal":
                            panelString.Visibility = System.Windows.Visibility.Collapsed;
                            panelNumber.Visibility = System.Windows.Visibility.Visible;
                            panelBoolean.Visibility = System.Windows.Visibility.Collapsed;
                            panelDate.Visibility = System.Windows.Visibility.Collapsed;
                            break;
                        case "Boolean":
                            panelString.Visibility = System.Windows.Visibility.Collapsed;
                            panelNumber.Visibility = System.Windows.Visibility.Collapsed;
                            panelBoolean.Visibility = System.Windows.Visibility.Visible;
                            panelDate.Visibility = System.Windows.Visibility.Collapsed;
                            break;
                        case "DateTime":
                            panelString.Visibility = System.Windows.Visibility.Collapsed;
                            panelNumber.Visibility = System.Windows.Visibility.Collapsed;
                            panelBoolean.Visibility = System.Windows.Visibility.Collapsed;
                            panelDate.Visibility = System.Windows.Visibility.Visible;
                            break;
                        default:
                            panelString.Visibility = System.Windows.Visibility.Collapsed;
                            panelNumber.Visibility = System.Windows.Visibility.Collapsed;
                            panelBoolean.Visibility = System.Windows.Visibility.Collapsed;
                            panelDate.Visibility = System.Windows.Visibility.Collapsed;
                            break;
                    }

                    UpdateDetailDisplay(variableName);
                }
            }
        }

        private struct WorkParameters
        {
            public string VariableName;
            public string DataType;
            public BackgroundWorker Worker;
        }

        private struct DateResults
        {
            public WorkParameters WorkParameters;
            public DateTime? Min;
            public DateTime? Max;
        }

        private struct NumericResults
        {
            public WorkParameters WorkParameters;
            public double? Min;
            public double? Max;
            public double? Sum;
            public double? Mean;
        }

        private struct BooleanResults
        {
            public WorkParameters WorkParameters;
            public int? True;
            public int? False;
            public double? TruePercent;
            public double? FalsePercent;
        }

        private void UpdateDetailDisplay(string variableName)
        {
            if (worker == null)
            {
                worker = new BackgroundWorker();
                worker.WorkerSupportsCancellation = true;
                worker.WorkerReportsProgress = false;
                worker.DoWork += new DoWorkEventHandler(worker_DoWork);
                worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            }
            else if (worker.WorkerSupportsCancellation && worker.CancellationPending)
            {
                worker.CancelAsync();
            }

            WorkParameters parameters = new WorkParameters();
            parameters.VariableName = variableName;
            parameters.DataType = DashboardHelper.GetColumnType(variableName);
            parameters.Worker = worker;

            worker.RunWorkerAsync(parameters);
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Result is NumericResults)
            {
                this.Dispatcher.BeginInvoke(new DisplayNumericResultsDelegate(DisplayNumericResults), (NumericResults)e.Result);
            }
            else if (e.Result is BooleanResults)
            {
                this.Dispatcher.BeginInvoke(new DisplayBooleanResultsDelegate(DisplayBooleanResults), (BooleanResults)e.Result);
            }
            else if (e.Result is DateResults)
            {
                this.Dispatcher.BeginInvoke(new DisplayDateResultsDelegate(DisplayDateResults), (DateResults)e.Result);
            }
        }

        private void DisplayNumericResults(NumericResults results)
        {
            if (results.Min.HasValue && !Double.IsNaN(results.Min.Value))
            {
                lblMin.Content = "Min: " + Math.Round(results.Min.Value, 2).ToString();
                lblMax.Content = "Max: " + Math.Round(results.Max.Value, 2).ToString();
                lblSum.Content = "Sum: " + Math.Round(results.Sum.Value, 2).ToString();
                lblMean.Content = "Mean: " + Math.Round(results.Mean.Value, 2).ToString();
            }
        }

        private void DisplayDateResults(DateResults results)
        {
            if (results.Min.HasValue)
            {
                lblMinDate.Content = "Min: " + results.Min.Value.ToShortDateString();
                lblMaxDate.Content = "Max: " + results.Max.Value.ToShortDateString();
            }
        }

        private void DisplayBooleanResults(BooleanResults results)
        {
            if (results.True.HasValue)
            {
                lblTrue.Content = "True: " + results.True.Value + " " + "(" + results.TruePercent.Value.ToString("P2") + ")";
            }
            if (results.False.HasValue)
            {
                lblFalse.Content = "False: " + results.False.Value + " " + "(" + results.FalsePercent.Value.ToString("P2") + ")";
            }
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {   
            WorkParameters parameters = (WorkParameters)(e.Argument);
            string varName = parameters.VariableName;
            string dataType = parameters.DataType;

            double count = 0;

            DataView dv = DashboardHelper.GenerateView();

            switch (dataType)
            {
                case "System.String":
                    // do nothing
                    break;
                case "System.Byte":
                case "System.Int16":
                case "System.Int32":
                case "System.Int64":
                case "System.Single":
                case "System.Double":
                case "System.Decimal":
                    double min = Double.NaN;
                    double max = Double.NaN;
                    double mean = Double.NaN;
                    double sum = Double.NaN;
                    count = 0;

                    foreach (DataRowView rowView in dv)
                    {
                        DataRow row = rowView.Row;

                        if (row[varName] != DBNull.Value)
                        {
                            double value = Convert.ToDouble(row[varName]);

                            if (value > max || Double.IsNaN(max))
                            {
                                max = value;
                            }

                            if (value < min || Double.IsNaN(min))
                            {
                                min = value;
                            }

                            if (Double.IsNaN(sum))
                            {
                                sum = value;
                            }
                            else
                            {
                                sum = sum + value;
                            }
                            count++;
                        }                        

                        if (worker.WorkerSupportsCancellation && worker.CancellationPending)
                        {
                            e.Result = null;
                            return;
                        }
                    }

                    mean = sum / count;

                    NumericResults numericResults = new NumericResults();
                    numericResults.WorkParameters = parameters;
                    numericResults.Max = max;
                    numericResults.Min = min;
                    numericResults.Sum = sum;
                    numericResults.Mean = mean;

                    if (worker.WorkerSupportsCancellation && !worker.CancellationPending)
                    {
                        e.Result = numericResults;
                        return;
                    }

                    break;
                case "System.DateTime":
                    DateTime? minDate = null;
                    DateTime? maxDate = null;

                    foreach (DataRowView rowView in dv)
                    {
                        DataRow row = rowView.Row;

                        if (row[varName] != DBNull.Value)
                        {
                            DateTime value = (DateTime)row[varName];

                            if (!maxDate.HasValue || value > maxDate.Value)
                            {
                                maxDate = value;
                            }

                            if (!minDate.HasValue || value < minDate.Value)
                            {
                                minDate = value;
                            }
                        }

                        if (worker.WorkerSupportsCancellation && worker.CancellationPending)
                        {
                            e.Result = null;
                            return;
                        }
                    }

                    DateResults dateResults = new DateResults();
                    dateResults.WorkParameters = parameters;
                    dateResults.Max = maxDate;
                    dateResults.Min = minDate;

                    if (worker.WorkerSupportsCancellation && !worker.CancellationPending)
                    {
                        e.Result = dateResults;
                        return;
                    }

                    break;
                case "System.Boolean":
                    int t = 0;
                    int f = 0;

                    count = 0;
                    foreach (DataRowView rowView in dv)
                    {
                        DataRow row = rowView.Row;

                        if (row[varName] != DBNull.Value)
                        {
                            bool value = Convert.ToBoolean(row[varName]);
                            if (value == true)
                            {
                                t = t + 1;
                            }
                            else if (value == false)
                            {
                                f = f + 1;
                            }
                        }

                        if (worker.WorkerSupportsCancellation && worker.CancellationPending)
                        {
                            e.Result = null;
                            return;
                        }
                        count++;
                    }

                    BooleanResults booleanResults = new BooleanResults();
                    booleanResults.WorkParameters = parameters;
                    booleanResults.True = t;
                    booleanResults.False = f;

                    if (count > 0)
                    {
                        if (booleanResults.True.HasValue)
                        {
                            booleanResults.TruePercent = booleanResults.True / count;
                        }
                        if (booleanResults.False.HasValue)
                        {
                            booleanResults.FalsePercent = booleanResults.False / count;
                        }
                    }

                    if (worker.WorkerSupportsCancellation && !worker.CancellationPending)
                    {
                        e.Result = booleanResults;
                        return;
                    }

                    break;
                    
            }
        }
    }
}
